//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "UI/Widgets/Button.h"

namespace Engine
{
namespace UI
{

	class RadioButton;
	SHARED_POINTER( RadioButton );



	//
	// Radio Button
	//

	class RadioButton : public Button
	{
		friend struct ::MetaData::RadioButtonCfg;

	// types
	public:

		//
		// Radio Button Control
		//
		class RadioButtonControl : public Referenced
		{
		// variables
		private:
			array< Ptr< RadioButton > >		_buttons;
			usize							_selected;
			UIAction						_checkedChanged;

		// methods
		public:
			RadioButtonControl () : _selected(-1) {}

			usize Add (RadioButton *rb);
			void  Remove (RadioButton *rb);

			void  OnChanged (usize idx);
		};

		friend class RadioButtonControl;

		SHARED_POINTER( RadioButtonControl );


	// variables
	protected:
		RadioButtonControlPtr	_control;
		usize					_index;
		UIAction				_checkedChanged;


	// methods
	public:
		RadioButton (EWidget::type wgtType, const EngineSubSystemsRef ess);

		~RadioButton ();
		
		uint GetIndex ()	const	{ return _index; }

		virtual bool ProcessMessage (const WidgetMsg &msg) override;

		
		static RadioButtonPtr	New (const EngineSubSystemsRef ess);


	protected:
		bool _OnLoad (const WidgetMsg &msg);
		bool _Update (const WidgetMsg &msg);
		bool _SetState (const WidgetMsg &msg);
	};


	
	
//------------------------ RadioButtonControl----------------------//
/*
=================================================
	Add
=================================================
*/
	inline usize RadioButton::RadioButtonControl::Add (RadioButton *rb)
	{
		if ( _selected == -1 and rb->_checked )
			_selected = _buttons.Count();
		else
			rb->_checked = false;

		if ( _checkedChanged.Empty() ) {
			_checkedChanged = rb->_checkedChanged;
		}
		else
		if ( _checkedChanged != rb->_checkedChanged ) {
			WARNING( "" );
		}

		_buttons.PushBack( rb );
		return _buttons.LastIndex();
	}
	
/*
=================================================
	Remove
=================================================
*/
	inline void RadioButton::RadioButtonControl::Remove (RadioButton *rb)
	{
		if ( rb->_index == _selected )
			_selected = _buttons.LastIndex();

		if ( rb->_index < _selected )
			_selected = Max( _buttons.LastIndex(), _selected-1 );

		_buttons.FindAndErase( rb );
	}
	
/*
=================================================
	OnChanged
=================================================
*/
	inline void RadioButton::RadioButtonControl::OnChanged (usize idx)
	{
		const bool	checked = _buttons[idx]->_checked;

		if ( not checked )
		{
			if ( _selected == idx )
				_selected = -1;
			
			_checkedChanged.Call( _buttons[idx] );
			return;
		}

		FOR( i, _buttons )
		{
			if ( i == idx )
				continue;

			const bool	checked = _buttons[i]->_checked;

			_buttons[i]->_checked = false;

			if ( checked ) {
				_checkedChanged.Call( _buttons[i] );
			}
		}

		_selected = idx;
		_checkedChanged.Call( _buttons[idx] );
	}
	

	

//------------------------- RadioButton ---------------------------//
/*
=================================================
	constructor
=================================================
*/
	inline RadioButton::RadioButton (EWidget::type wgtType, const EngineSubSystemsRef ess) :
		Button(wgtType, ess), _index(-1)
	{
		_checkedState	= true;
	}
	
/*
=================================================
	destructor
=================================================
*/
	inline RadioButton::~RadioButton ()
	{
	}
		
/*
=================================================
	ProcessMessage
=================================================
*/
	inline bool RadioButton::ProcessMessage (const WidgetMsg &msg)
	{
		typedef WidgetMsg	WM;

		switch ( msg.GetType() )
		{
			case WM::UPDATE :		return _Update( msg );
			case WM::ON_LOAD :		return _OnLoad( msg );
			case WM::SET_STATE :	return _SetState( msg );
		}

		return Button::ProcessMessage( msg );
	}
		
/*
=================================================
	New
=================================================
*/
	inline RadioButtonPtr RadioButton::New (const EngineSubSystemsRef ess)
	{
		return BaseObject::_New( new RadioButton( EWidget::RADIO_BUTTON, ess ) );
	}
	
/*
=================================================
	_OnLoad
=================================================
*/
	inline bool RadioButton::_OnLoad (const WidgetMsg &msg)
	{
		CHECK_ERR( GetParent().IsNotNull() );

		const widget_array_t &	widgets = GetParent()->GetWidgets();

		FOR( i, widgets )
		{
			if ( widgets[i]->GetType() == EWidget::RADIO_BUTTON )
			{
				RadioButton *	rb = widgets[i].ToPtr< RadioButton >();

				if ( rb->_control.IsNotNull() )
				{
					_control = rb->_control;
					_index   = _control->Add( this );
					break;
				}
			}
		}

		if ( _control.IsNull() )
		{
			_control = new RadioButtonControl();
			_index   = _control->Add( this );
		}

		return Button::_OnLoad( msg );
	}
	
/*
=================================================
	_Update
=================================================
*/
	inline bool RadioButton::_Update (const WidgetMsg &msg)
	{
		typedef WidgetMsg	WM;

		if ( not IsVisible() )
			return true;
		
		WM::Update const &		upd			= msg.To< WM::Update >();
		ColorStyle::EType const	prev_state	= _state;

		if ( _enabled )
		{
			const bool	is_inner = _region.IntersectRect( upd.data->TouchArea() );

			if ( is_inner and upd.data->IsPressed() )
			{
				upd.data->ResetTouch();
				_state = _checked ? ColorStyle::CHECKED_PRESSED : ColorStyle::IDLE_PRESSED;
			}
			else
			if ( is_inner and upd.data->IsClicked() )
			{
				upd.data->ResetTouch();

				if ( _checkedState )
				{
					_checked = not _checked;
					_state   = _checked ? ColorStyle::CHECKED : ColorStyle::IDLE;
				}

				_onClick.Call( this );
				_control->OnChanged( _index );
			}
			else
				_state = _checked ? ColorStyle::CHECKED : ColorStyle::IDLE;
		}
		else
			_state = ColorStyle::DISABLED;

		_stateChanged = (prev_state != _state);
		_CheckChangedOnUpdate( msg );
					
		_SendToAllAttached( msg );
		return true;
	}
	
/*
=================================================
	_SetState
=================================================
*/
	inline bool RadioButton::_SetState (const WidgetMsg &msg)
	{
		typedef WidgetMsg	WM;

		WM::SetState const &	st = msg.To< WM::SetState >();

		switch ( st.state )
		{
			case WM::SetState::ENABLED :	_enabled = st.value;	break;
			case WM::SetState::VISIBLE :	_visible = st.value;	break;
			case WM::SetState::CHECKED :	_checked = st.value;	_control->OnChanged( _index );	break;
		}

		if ( not _enabled )
			_state = ColorStyle::DISABLED;
		else
			_state = _checked ? ColorStyle::CHECKED : ColorStyle::IDLE;

		if ( st.recursive ) {
			_SendToAllAttached( msg );
		}

		_stateChanged = true;
		return true;
	}


}	// UI
}	// Engine